#include "scheduler.h"
#include <time.h>

int HRRNScheduler::create(int ppid, const std::vector<Command> &commands, int pc, PCB::Priority priority)               //创建新的进程
{
    int pid = -1;                                                                                                       //初始化pid
    for (int i = 1; i < 65536; i++)
    {
        if (PCB_table.find(i) == PCB_table.end())
        {
            pid = i;
            break;
        }
    }                                                                                                                   //找到未使用的pid
    if (pid == -1)
        return -1;                                                                                                      //没有未使用的pid，返回-1，代表创建失败
                                                                                                                        //以下为成功创建进程的情况
    auto PCB_ptr = std::make_shared<PCB>(PCB{                                                                           //构造新进程的PCB
        ppid,                                                                                                           //父进程的pid
        pid,                                                                                                            //其本身的pid
        PCB::NEW,                                                                                                       //PCB初始状态为new
        pc,                                                                                                             //该进程即将运行的指令的位置
        commands,                                                                                                       //该进程的命令集
        PCB::Priority{                                                                                                  //该进程的优先级
            0}});

    PCB_table.insert({pid, PCB_ptr});
    new_queue.push_back(pid);                                                                                           //将进程加入到new队列中

    return pid;                                                                                                         //返回该进程pid的值
}

int HRRNScheduler::admit(int pid)                                                                                       //将pid号进程选入ready队列
{
    auto it = new_queue.begin();
    bool found = false;

    for (; it != new_queue.end(); it++)                                                                                 //遍历new队列
    {
        if (*it == pid)
        {
            found = true;
            break;
        }
    }

    if (!found)                                                                                                         //如果没有与需要pid匹配的进程，该函数失败，返回-1
    {
        return -1;
    }
                                                                                                                        //下面为找到与之匹配的情况
    ready_queue.push_back(*it);                                                                                         //将选中的进程加入ready队列
    PCB_table[pid]->priority.time = time(0);                                                                            //记录下进入等待队列的时间
    new_queue.erase(it);                                                                                                //在new队列中删除该进程
    return 0;
}

int HRRNScheduler::get_task()
{
    while (!new_queue.empty() && (max_progress_count <= 0 || ready_queue.size() < max_progress_count))                  //当new队列不为空而ready队列的大小小于最大可同时运行进程数时
                                                                                                                        //将自动将new队列中队首的进程移入ready队列中
                                                                                                                        //直到new队列为空或者ready队列的大小大于最大可同时运行进程数
    {
        int pid = new_queue.front();
        ready_queue.push_back(pid);
        PCB_table[pid]->priority.time = time(0);
        new_queue.pop_front();
    }

    if (ready_queue.size() == 0)                                                                                        //此种情况代表new队列和ready队列都为空，此时无法调度新的进程进入run状态
        return -1;
                                                                                                                        //下面为调度器调度的过程，采用HRRN最高响应比调度算法
    int result;                                                                                                         //记录调度器调度结果
    double max_r = 0;                                                                                                   //当前最高响应比
    time_t now = time(0);                                                                                               //记录当前的时间
    auto it = ready_queue.begin();
    for (; it != ready_queue.end(); it++)                                                                               //对ready队列进行遍历
    {
        double w_time = now - PCB_table[*it]->priority.time;                                                            //等待时间为当前时间-该进程进入ready队列的时间
        double t_time = 10 * (PCB_table[*it]->commands.size() - PCB_table[*it]->pc);                                    //需要占用CPU的时间用该进程剩余指令数来预估
        double temp_r = 1 + w_time / t_time;                                                                            //根据公式计算出该进程的响应比
        if (max_r < temp_r)                                                                                           //如果大于当前最高响应比更新最高响应比
        {
            max_r = temp_r;
            result = *it;
        }
    }

    return result;                                                                                                      //返回调度出的最高响应比的进程
}

int HRRNScheduler::set_run(int pid)                                                                                     //将pid号的进程设置为run状态
{
    auto it = ready_queue.begin();
    bool found = false;

    for (; it != ready_queue.end(); it++)                                                                               //遍历ready队列寻找与pid匹配的进程
    {
        if (*it == pid)
        {
            found = true;
            break;
        }
    }

    if (!found)                                                                                                         //没有在ready队列中找到，返回-1，设置失败
    {
        return -1;
    }
                                                                                                                        //以下为找到的情况
    ready_queue.erase(it);                                                                                              //将其从ready队列中删除
    PCB_table[pid]->state = PCB::RUN;                                                                                   //改变其进程状态
    run_queue.push_back(pid);                                                                                           //加入run队列中

    return 0;
}

int HRRNScheduler::set_ready(int pid)                                                                                   //将pid号进程设置为ready状态
{
    auto it = wait_queue.begin();
    bool found = false;

    for (; it != wait_queue.end(); it++)                                                                                //将wait状态的进程转化为ready状态
    {
        if (*it == pid)
        {
            found = true;
            break;
        }
    }

    if (found)
    {
        wait_queue.erase(it);
        PCB_table[pid]->state = PCB::READY;
        ready_queue.push_back(pid);
        PCB_table[pid]->priority.time = time(0);
        return 0;
    }

    it = run_queue.begin();                                                                                             //将run状态的进程设置为ready状态
    found = false;

    for (; it != run_queue.end(); it++)
    {
        if (*it == pid)
        {
            found = true;
            break;
        }
    }

    if (found)
    {
        run_queue.erase(it);
        PCB_table[pid]->state = PCB::READY;
        ready_queue.push_back(pid);
        PCB_table[pid]->priority.time = time(0);
        return 0;
    }
                                                                                                                        //在两个队列中都没有找到相应进程返回-1代表失败
    return -1;
}

int HRRNScheduler::set_wait(int pid)                                                                                    //将pid号进程转化为wait状态
{
    auto it = run_queue.begin();
    bool found = false;

    for (; it != run_queue.end(); it++)
    {
        if (*it == pid)
        {
            found = true;
            break;
        }
    }

    if (!found)
    {
        return -1;
    }

    run_queue.erase(it);

    PCB_table[pid]->state = PCB::WAIT;
    run_queue.push_back(pid);

    return 0;
}

int HRRNScheduler::exit(int pid)                                                                                        //pid号进程退出
{
    return kill(pid);                                                                                                   //调用kill函数
}

int HRRNScheduler::kill(int pid)
{
    PCB_table.erase(pid);                                                                                               //先将其从PCB_table中删除

    for (auto it = new_queue.begin(); it != new_queue.end(); it++)                                                      //分别在new队列，ready队列，run队列，wait队列中寻找该进程，若找到删除，函数执行成功
    {
        if (*it == pid)
        {
            new_queue.erase(it);
            return 0;
        }
    }
    for (auto it = ready_queue.begin(); it != ready_queue.end(); it++)
    {
        if (*it == pid)
        {
            ready_queue.erase(it);
            return 0;
        }
    }
    for (auto it = run_queue.begin(); it != run_queue.end(); it++)
    {
        if (*it == pid)
        {
            run_queue.erase(it);
            return 0;
        }
    }
    for (auto it = wait_queue.begin(); it != wait_queue.end(); it++)
    {
        if (*it == pid)
        {
            wait_queue.erase(it);
            return 0;
        }
    }

    return -1;                                                                                                          //若都没找到，返回-1，代表不存在该进程
}

std::shared_ptr<PCB> HRRNScheduler::get_PCB(int pid)                                                                    //根据pid号获得其对应的PCB号
{
    decltype(PCB_table)::iterator it;                                                                                   //直接在PCB_table中查找
    if ((it = PCB_table.find(pid)) != PCB_table.end())                                                                  //找到返回其PCB
    {
        return it->second;
    }
    else
    {
        return nullptr;                                                                                                 //没有找到返回空指针
    }
}

#ifdef UNIT_TEST

#include <gtest/gtest.h>

TEST(SCHEDULER_TEST, HRRN_TEST)
{
    Scheduler *s = new HRRNScheduler();

    int pid[4];

    for (int i = 0; i < 4; i++)
        pid[i] = s->create(0, {}, 0, {0});

    for (int i = 0; i < 4; i++)
    {
        EXPECT_EQ(pid[i], s->get_task());
        s->set_run(pid[i]);
    }
}

#endif
